/* Copyright (C) 2004,2005  The DESIROS Team
    desiros.dev@gmail.com

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
   USA. 
 */

#include <types.h>
#include "stdarg.h"

int vsnprintf(char *buff, size_t len, const char * format, va_list ap)
{
  int i, result;
  int fmt_modifiers = 0;
  int prefix_long   = 0;
  int prefix_long_long = 0;
 
  if (!buff || !format || (len < 0))
    return -1;
  
#define PUTCHAR(thechar) \
  do { \
    if (result < len-1) \
      *buff++ = (thechar);  \
    result++; \
  } while (0)
  
  result = 0;
  for(i=0 ; format[i] != '\0' ; i++)
    {
      if (!fmt_modifiers && (format[i] != '%'))
	{
	  PUTCHAR(format[i]);
	  continue;
	}
	
      switch (format[i])
	{
	case '%':
	  if (fmt_modifiers)
	    {
	      PUTCHAR('%');
	      fmt_modifiers = 0;
	      break;
	    }
	  
	  fmt_modifiers    = 1;
	  prefix_long      = 0;
	  prefix_long_long = 0;
	  break;
	  
	case 'l':
	  if (prefix_long)
	    prefix_long_long = 1;
	  else
	    prefix_long = 1;
	  break;
	  
	case 'u':
	  {
	    if (! prefix_long_long)
	      {
		unsigned int integer = va_arg(ap,unsigned int);
		int cpt2 = 0;
		char buff_int[16];
		
		do {
		  int m10 = integer%10;
		  buff_int[cpt2++]=(char)('0'+ m10);
		  integer=integer/10;
		} while(integer!=0);
	    
		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
		  PUTCHAR(buff_int[cpt2]);
	      }
	    else
	      {
		unsigned long long int integer
		  = va_arg(ap,unsigned long long int);
		int cpt2 = 0;
		char buff_int[32];
		
		do {
		  int m10 = integer%10;
		  buff_int[cpt2++]=(char)('0'+ m10);
		  integer=integer/10;
		} while(integer!=0);
	    
		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
		  PUTCHAR(buff_int[cpt2]);
	      }
	  }
	  fmt_modifiers = 0;
	  break;

	case 'i':
	case 'd':
	  {
	    if (! prefix_long_long)
	      {
		int integer = va_arg(ap,int);
		int cpt2 = 0;
		char buff_int[16];
		
		if (integer<0)
		  PUTCHAR('-');
		/* Ne fait pas integer = -integer ici parce que INT_MIN
		   n'a pas d'equivalent positif (int = [-2^31, 2^31-1]) */
		
		do {
		  int m10 = integer%10;
		  m10 = (m10 < 0)? -m10:m10;
		  buff_int[cpt2++]=(char)('0'+ m10);
		  integer=integer/10;
		} while(integer!=0);
	    
		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
		  PUTCHAR(buff_int[cpt2]);
	      }
	    else
	      {
		long long int integer = va_arg(ap,long long int);
		int cpt2 = 0;
		char buff_int[32];
		
		if (integer<0)
		  PUTCHAR('-');
		/* Ne fait pas integer = -integer ici parce que INT_MIN
		   n'a pas d'equivalent positif (int = [-2^63, 2^63-1]) */
		
		do {
		  int m10 = integer%10;
		  m10 = (m10 < 0)? -m10:m10;
		  buff_int[cpt2++]=(char)('0'+ m10);
		  integer=integer/10;
		} while(integer!=0);
	    
		for(cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--)
		  PUTCHAR(buff_int[cpt2]);
	      }
	  }
	  fmt_modifiers = 0;
	  break;
	  
	case 'c':
	  {
	    int value = va_arg(ap,int);
	    PUTCHAR((char)value);
	    fmt_modifiers = 0;
	    break;
	  }
	  
	case 's':
	  {
	    char *string = va_arg(ap,char *);
	    if (! string)
	      string = "(null)";
	    for( ; *string != '\0' ; string++)
	      PUTCHAR(*string);
	    fmt_modifiers = 0;
	    break;
	  }
	  
	case 'p':
	  PUTCHAR('0');
	  PUTCHAR('x');
	case 'x':
	  {
	    unsigned long long int hexa;
	    unsigned long long int nb;
	    int i, had_nonzero = 0;
	    
	    if (prefix_long_long)
	      hexa = va_arg(ap,unsigned long long int);
	    else
	      hexa = va_arg(ap,unsigned int);
	    
	    for(i=0 ; i < 16 ; i++)
	      {
		nb = (unsigned long long int)(hexa << (i*4));
		nb = (nb >> 60) & 0xf;
		// Skip the leading zeros
		if (nb == 0)
		  {
		    if (had_nonzero)
		      PUTCHAR('0');
		  }
		else
		  {
		    had_nonzero = 1;
		    if (nb < 10)
		      PUTCHAR('0'+nb);
		    else
		      PUTCHAR('a'+(nb-10));
		  }
	      }
	    if (! had_nonzero)
	      PUTCHAR('0');
	  }
	  fmt_modifiers = 0;
	  break;
	  
	default:
	  PUTCHAR('%');
	  if (prefix_long)
	    PUTCHAR('l');
	  if (prefix_long_long)
	    PUTCHAR('l');
	  PUTCHAR(format[i]);
	  fmt_modifiers = 0;
	}
    }

  *buff = '\0';
  return result;
}


int sprintf(char * buff, const char *format, ...)
{
  va_list ap;
 size_t len = strlen(buff) ;

  va_start(ap, format);
  len = vsnprintf(buff, len, format, ap);
  va_end(ap);
 
  return len;
}




